<?php

/**
 * Базовый класс списков с возможностью фильтрования записей.
 *
 */
abstract class Kernel_Component_FilteredList extends Kernel_Action_Abstract implements Kernel_Form_Storage {

    /**
     * Наименование таблицы (представления), из которой нужно получать данные.
     *
     * @var string
     */
    protected $_table = '';

    /**
     * Наименования полей таблицы "ключ => значение" для получения доступных
     * значений для параметров-списков.
     *
     * @var array
     */
    protected $_lists = array();

    /**
     * Загружает данные формы.
     *
     * @return  array
     */
    public function load() {
        return array();
    }

    /**
     * Сохраняет данные формы.
     *
     * @param   array   $values
     */
    public function save($values) {
        $this->_call('list', $this->_getListParams($values));
    }

    /**
     * Временно. Вызывается после сохранения данных формы.
     *
     */
    public function done() { }

    /**
     * Реализация обработки пользовательского запроса.
     *
     */
    protected function _process() {
        $this->_call('form', $this->_getFormParams());

        // :KLUDGE: morozov 17012008: надо подумать насчет выставления
        // шаблона страницы несколькими компонентами действий
        Application::getResponse()->setTemplate($this->_template);
    }

    /**
     * Возвращает параметры элементов формы настройки фильтра.
     *
     * @return  array
     */
    protected function _elements() {
        $elements = array();
        foreach ($this->_getFilterValues() as $name => $values) {
        	$elements[$name] = array('values' => $values);
        }
        return $elements;
    }

    /**
     * Возвращает возможные значения для параметров фильтрации — списков.
     *
     * @return  array
     */
    protected function _getFilterValues() {
        $values = array();
        foreach ($this->_lists as $key => $value) {
            $values[$key] = $this->_getValues($key, $value);
        }
        return $values;
    }

    /**
     * Возвращает базовые значения параметров фильтрации.
     *
     * @return  array
     */
    abstract protected function _base();

    /**
     * Возвращает парамеры, которые нужно передать компоненту-форме.
     *
     * @return  array
     */
    protected function _getFormParams() {
        return array(
            'storage' => $this,
            'elements' => $this->_elements(),
        );
    }

    /**
     * Возвращает парамеры, которые нужно передать компоненту-списку.
     *
     * @param   array   $values
     * @return  array
     */
    protected function _getListParams($values) {
        return array(
            'filter' => $this->_combine($values),
            'table' => $this->_table,
        );
    }

    /**
     * Вызывает вложенный компонент указанного наименования.
     *
     * @param   string  $name
     * @param   params  $params
     */
    private function _call($name, $params = array()) {
        $this->_forward(Kernel::factory(get_class($this), $name, $params));
    }

    /**
     * Собирает массив параметров фильтрации списка из параметров по умолчанию
     * и полученных из формы.
     *
     * :TODO: morozov 21012008: закрыть обратно, сделать так, чтобы для
     * полей-диапазонов форма возвращала значение-диапазон
     *
     * @param   array   $values
     * @return  array
     */
    protected /* private */function _combine($values) {
        $params = array(); $base = $this->_base();
        foreach ($values as $param => $value) {
            $params[$param] = !$value && isset($base[$param])
                ? $base[$param] : $value;
        }
        return $params;
    }

    /**
     * Возвращает список доступных значений указанного параметра.
     *
     * @param   string  $param
     * @return  array
     */
    private function _getValues($key, $value) {
        $query = 'SELECT DISTINCT
                         `' . $key . '`, `' . $value . '`
                    FROM
                         `' . $this->_table . '`
                   WHERE
                         ' . database::condition($this->_base());
        return database::fetchPairs($query);
    }
}
